using System;
using System.Collections.Generic;
using System.Linq;
using Castle.ActiveRecord;
using NHibernate.Criterion;

namespace DecisionMaking.Domain
{
	
	[ActiveRecord]
	public class Agent : ActiveRecordValidationBase<Agent>
	{
		private IList<CandidateCueValue> _candidateCueValues = new List<CandidateCueValue>();
        private IList<Candidate> _candidatesRecognized = new List<Candidate>();
        private IDictionary<Candidate, IList<CandidateCueValue>> _candidateCueMap;

        private readonly IDictionary<string, object> _context = new Dictionary<string, object>();
		
		public Agent ()
		{
		}
		
	    [PrimaryKey(PrimaryKeyType.Native)]
	    public int Id {set; get;}	
		
		[BelongsTo(NotNull = true)]
		public Environment Environment {set; get;}

        [HasAndBelongsToMany(typeof(Candidate), Table = "AgentCandidates", ColumnKey = "AgentId", ColumnRef = "CandidateId", Lazy = false)]
        public IList<Candidate> CandidatesRecognized
	    {
	        get { return _candidatesRecognized; }
	        set { _candidatesRecognized = value; }
	    }

	    [HasAndBelongsToMany(typeof(CandidateCueValue), Table="AgentCandidateCueValues", ColumnKey="AgentId", ColumnRef="CandidateCueValueId", Lazy = false)]
		public IList<CandidateCueValue> CandidateCueValues
		{
			get {return _candidateCueValues;}
			set {_candidateCueValues = value;}
		}

        [Property]
        public double PctOfCuesKnown { get; set; }
	
        /// <summary>
        /// Transient
        /// </summary>
	    public IDictionary<string, object> Context
	    {
            get { return _context; }
        }

        /// <summary>
        /// Retrieves a map of candidates to their cue values
        /// </summary>
        /// <returns></returns>
        public IDictionary<Candidate, IList<CandidateCueValue>> GetCandidateCueMap()
        {
            // lazy initialize dictionary
            if (_candidateCueMap == null)
            {
                _candidateCueMap = new Dictionary<Candidate, IList<CandidateCueValue>>();

                foreach (var value in CandidateCueValues)
                {
                    if (!_candidateCueMap.ContainsKey(value.Candidate))
                    {
                        _candidateCueMap.Add(value.Candidate, new List<CandidateCueValue>());
                    }

                    _candidateCueMap[value.Candidate].Add(value);
                }                
            }

            return _candidateCueMap;
        }

        /// <summary>
        /// Retrieves the cues for a specific candidate, or an empty list if not found
        /// </summary>
        /// <param name="candidate"></param>
        /// <returns></returns>
        public IList<CandidateCueValue> GetCues(Candidate candidate)
        {
            return GetCandidateCueMap().ContainsKey(candidate) ? GetCandidateCueMap()[candidate] : null;
        }

        /// <summary>
        /// Returns true if this agent recognizes the specified candidate
        /// </summary>
        /// <param name="candidate"></param>
        /// <returns></returns>
        public bool Recognizes(Candidate candidate)
        {
            return CandidatesRecognized.Contains(candidate);
        }

        /// <summary>
        /// Retreives the cue value for the specified candidate or null if not recognized
        /// </summary>
        /// <param name="candidate"></param>
        /// <param name="cue"></param>
        /// <returns></returns>
        public double? GetCueValue(Candidate candidate, Cue cue)
        {
            var cues = GetCues(candidate);

            if (cues == null)
            {
                return null;
            }
            else
            {
                return (from c in cues where c.Cue.Equals(cue) select c.Value).SingleOrDefault();
            }
        }

        /// <summary>
        /// Returns a list of agent for the specified environment
        /// </summary>
        /// <param name="environmentId"></param>
        /// <returns></returns>
        public static IList<Agent> FindByEnvironmentId(int environmentId)
        {
            DetachedCriteria criteria = DetachedCriteria.For(typeof(Agent));

            criteria.CreateCriteria("Environment").Add(Restrictions.IdEq(environmentId));

            return FindAll(criteria);            
        }

        /// <summary>
        /// Deletes all agents in the specified environment
        /// </summary>
        /// <param name="environmentId"></param>
        public static void DeleteAllInEnvironment(int environmentId)
        {
            Agent.DeleteAll("Environment.id = " + environmentId);
        }
		
		public override bool Equals (object obj)
		{
			if (obj == null)
				return false;
			if (ReferenceEquals (this, obj))
				return true;
			if (obj.GetType () != typeof(Agent))
				return false;
			DecisionMaking.Domain.Agent other = (DecisionMaking.Domain.Agent)obj;
			return Id == other.Id;
		}


		public override int GetHashCode ()
		{
			unchecked {
				return Id.GetHashCode ();
			}
		}

	}
}

